package mango.orm.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import mango.Mango;
import mango.orm.Dao;
import mango.orm.DaoFactory;
import mango.orm.OrmException;
import mango.orm.OrmFactory;
import mango.orm.DBSession;
import mango.orm.DBSessionFactory;
import mango.orm.connect.Connector;
import mango.orm.entity.Entity;

public class DBSessionImpl implements DBSession {
    private List<Statement> statements;
    private List<ResultSet> resultSets;
    private Connector conn;
    private ArrayList<Object> handleList;
    private ArrayList<Dao> daoList;
    private boolean closed;
    private String dbType;
    private String connName;

    public DBSessionImpl(Connector conn) {
        if (conn == null) {
            throw new NullPointerException("Connector can't be null!");
        }
        this.statements = new ArrayList<Statement>();
        this.resultSets = new ArrayList<ResultSet>();
        this.conn = conn;
        this.handleList = new ArrayList<Object>();
        this.daoList = new ArrayList<Dao>();
        this.closed = false;
        this.dbType = this.conn.getDbType();
        this.connName = this.conn.getConnName();
    }

    public Connector getConnector() {
        this.check();
        return this.conn;
    }
    
    public String getConnectorName() {
        return this.connName;
    }

    public Dao query(String sql) {
        this.check();
        Dao dao = DaoFactory.c(this, sql);
        return dao;
    }

    public Dao query(Class<? extends Entity> bc, String sql) {
        this.check();
        Dao dao = DaoFactory.c(this, sql);
        dao.setResultType(bc);
        return dao;
    }

    public Dao query(Entity entity) {
        this.check(entity);
        Dao dao = DaoFactory.c(this, OrmFactory.getSQLMaker(this.dbType).getQuerySql(entity));
        dao.setResultType(entity.getClass());
        dao.setParameter(entity);
        return dao;
    }

    public void save(Entity entity) {
        if (!this.check(entity)) {
            Mango.getLog().warn(entity + " save skiped!!!");
            return;
        }
        Dao dao = DaoFactory.c(this, OrmFactory.getSQLMaker(this.dbType).getInsertSql(entity));
        dao.setResultType(entity.getClass());
        dao.setParameter(entity);
        this.handleList.add(entity);
        this.daoList.add(dao);
    }
    
    public void save(List<? extends Entity> entitys) {
        if (!this.check(entitys)) {
            Mango.getLog().warn(entitys + " save skiped!!!");
            return;
        }
        Dao dao = DaoFactory.c(this, OrmFactory.getSQLMaker(this.dbType).getInsertSql(entitys.get(0)));
        dao.setResultType(entitys.get(0).getClass());
        for (Entity entity : entitys) {
            dao.setParameter(entity);
        }
        this.handleList.add(entitys);
        this.daoList.add(dao);
    }

    public void update(Entity entity) {
        if (!this.check(entity)) {
            Mango.getLog().warn(entity + " update skiped!!!");
            return;
        }
        Dao dao = DaoFactory.c(this, OrmFactory.getSQLMaker(this.dbType).getUpdateSql(entity));
        dao.setResultType(entity.getClass());
        dao.setParameter(entity);
        this.handleList.add(entity);
        this.daoList.add(dao);
    }

    public void saveOrUpdate(Entity entity) {
        if (!this.check(entity)) {
            Mango.getLog().warn(entity + " saveOrUpdate skiped!!!");
            return;
        }
        if (this.containsEntity(entity)) {
            this.update(entity);
        }
        else {
            this.save(entity);
        }
    }
    
    public void saveIfNotExists(Entity entity) {
        if (!this.check(entity)) {
            Mango.getLog().warn(entity + " saveIfNotExists skiped!!!");
            return;
        }
        if (!this.containsEntity(entity)) {
            this.save(entity);
        }
    }

    private boolean containsEntity(Entity entity) {
        this.check();
        Object o = this.query(entity).uniqueResult();
        if (o != null) {
            return true;
        }
        return false;
    }
    

    public void delete(Entity entity) {
        if (!this.check(entity)) {
            Mango.getLog().warn(entity + " delete skiped!!!");
            return;
        }
        Dao dao = DaoFactory.c(this, OrmFactory.getSQLMaker(this.dbType).getDeleteSql(entity));
        dao.setResultType(entity.getClass());
        dao.setParameter(entity);
        this.handleList.add(entity);
        this.daoList.add(dao);
    }

    public void update(String sql) {
        Dao dao = DaoFactory.c(this, sql);
        dao.setType(Dao.TYPE_UPDATE);
        this.handleList.add(sql);
        this.daoList.add(dao);
    }
    
    public void execute(String sql) {
        Dao dao = DaoFactory.c(this, sql);
        dao.setType(Dao.TYPE_OTHER);
        this.handleList.add(sql);
        this.daoList.add(dao);
    }
    
    public void update(String sql, Object... parameters) {
        Dao dao = DaoFactory.c(this, sql);
        dao.setType(Dao.TYPE_UPDATE);
        dao.setParameter(parameters);
        this.handleList.add(sql);
        this.daoList.add(dao);
    }

    public void execute(String sql, Object... parameters) {
        Dao dao = DaoFactory.c(this, sql);
        dao.setType(Dao.TYPE_OTHER);
        dao.setParameter(parameters);
        this.handleList.add(sql);
        this.daoList.add(dao);
    }

    public boolean isClosed() {
        return this.closed;
    }

    public void commit() {
        this.check();
        try {
            this.conn.startTransaction();
            for (int i = 0; i < this.daoList.size(); i++) {
                DaoImpl dao = (DaoImpl) this.daoList.get(i);
                if (dao.isUsed()) {
                    continue;
                }
                Object v = this.handleList.get(i);
                if (v instanceof List) {
                    dao.executeBatch();
                }
                else if (v instanceof Entity) {
                    dao.executeUpdate();
                }
                else if (v instanceof String) {
                    dao.executeUpdate();
                }
                else {
                    Mango.getLog().error("error type--->" + v);
                }
            }
            this.conn.commit();
        }
        catch (Exception e) {
            this.rollback();
            throw new OrmException(e);
        }
        finally {
            DBSessionFactory.closeSession(this);
        }
    }
    
    public void rollback() {
        if (!this.isClosed() && this.conn != null) {
            try {
                this.conn.rollback();
            }
            catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public void close() {
        if (this.closed) {
            return;
        }
        for (ResultSet result : this.resultSets) {
            if (result != null) {
                try {
                    result.close();
                }
                catch (SQLException e) {
                }
            }
        }
        this.resultSets.clear();
        this.resultSets = null;
        for (Statement stat : this.statements) {
            if (stat != null) {
                try {
                    stat.close();
                }
                catch (SQLException e) {
                }
            }
        }
        this.statements.clear();
        this.statements = null;
        if (this.conn != null) {
            try {
                this.conn.close();
            }
            catch (SQLException e) {
                e.printStackTrace();
            }
        }
        this.conn = null;
        this.handleList.clear();
        this.handleList = null;
        this.daoList.clear();
        this.daoList = null;
        this.closed = true;
    }

    public void addStatement(Statement stat) {
        this.statements.add(stat);
    }

    public void addResultSet(ResultSet resultSet) {
        this.resultSets.add(resultSet);
    }

    private void check() {
        if (this.isClosed()) {
            throw new OrmException("this Session was closed!");
        }
    }
    
    private boolean check(Object entity) {
        check();
        if (entity == null) {
            return false;
        }
        if (entity instanceof List) {
            @SuppressWarnings("unchecked")
            List<Entity> ls = (List<Entity>) entity;
            if (ls.size() == 0) {
                return false;
            }
        }
        return true;
    }
}
